﻿using System;
using System.Collections.Generic;
using System.Linq;
using anydata.Loaders.Helpers;
using MongoDB.Driver;

namespace anydata.Loaders.Builders
{
    internal static class SortOptionBuilder<T>
    {
        private static readonly SortDefinitionBuilder<T> Builder = new SortDefinitionBuilder<T>();

        public static SortDefinition<T> Build(DataSourceLoadOptionsBase loadOptions, bool ensurePrimaryKeySorting)
        {
            if (loadOptions is null)
                throw new ArgumentNullException(nameof(loadOptions));

            // apply if sort
            if (loadOptions.sort != null && loadOptions.sort.Any())
                return CombineSortingInfos(loadOptions.sort);

            // if there is paging and no sorting is done, try to sort with the primary key
            if (ensurePrimaryKeySorting && (loadOptions.skip > 0 || loadOptions.take > 0))
                return SortByPrimaryKey(loadOptions);

            return null;
        }

        private static SortDefinition<T> SortByPrimaryKey(DataSourceLoadOptionsBase loadOptions)
        {
            string[] primaryKeys = loadOptions.primaryKey ?? ReflectionUtil.GetPrimaryKeysByType<T>();

            if (primaryKeys != null && primaryKeys.Any())
            {
                loadOptions.sortByPrimaryKey = true;
                var primaryKeySortingInfoList = new List<SortingInfo>();

                foreach (string primaryKey in primaryKeys)
                {
                    primaryKeySortingInfoList.Add(new SortingInfo()
                    {
                        selector = primaryKey,
                        desc = false
                    });
                }

                return CombineSortingInfos(primaryKeySortingInfoList);
            }

            return null;
        }

        private static SortDefinition<T> CombineSortingInfos(IEnumerable<SortingInfo> sortingInfos)
        {
            if (sortingInfos == null || !sortingInfos.Any())
                return null;

            var sortDefinitionList = new List<SortDefinition<T>>();

            foreach (var sortingInfo in sortingInfos)
            {
                SortDefinition<T> sortDefinition = sortingInfo.desc
                    ? Builder.Descending(sortingInfo.selector)
                    : Builder.Ascending(sortingInfo.selector);

                sortDefinitionList.Add(sortDefinition);
            }

            return Builder.Combine(sortDefinitionList);
        }
    }
}